﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Expect.Core.Reports;
using Expect.Core.IO;

namespace Expect.Core.CodeGen
{
    using System.CodeDom;
    using System.CodeDom.Compiler;
    using System.IO;
    using System.Reflection;
    using Expect.Core.BaseClasses;

    /// <summary>
    /// Code generation for report metadata
    /// </summary>
    public class ReportMetadataGenerator
    {
        private GenerationInfo _info;

        private string _inputfile;

        private ReportMetadataBase _data;

        private CodeDomHelper _codeDomHelper;

        /// <summary>
        /// Parse the content and return code
        /// </summary>
        /// <param name="info"></param>
        /// <param name="inputfile"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        public static string Execute(GenerationInfo info, string inputfile, string content)
        {
            InternalReportMetadata metadata = new InternalReportMetadata();
            TextReportMetadataReader reader = new TextReportMetadataReader(metadata);
            reader.ReadReportMetadata(content);
            ReportMetadataGenerator generator = new ReportMetadataGenerator(info, inputfile, metadata);

            return generator.Generate();
        }

        /// <summary>
        /// Generator constructor
        /// </summary>
        /// <param name="info"></param>
        /// <param name="inputfile"></param>
        /// <param name="data"></param>
        public ReportMetadataGenerator(GenerationInfo info, string inputfile, ReportMetadataBase data)
        {
            _info = info;
            _data = data;
            _inputfile = inputfile;
        }

        /// <summary>
        /// Generate code
        /// </summary>
        /// <returns></returns>
        public string Generate()
        {
            CodeNamespace codeNamespace = new CodeNamespace(_info.CodeFileNameSpace);
            _codeDomHelper = new CodeDomHelper(_info.Provider);

            StringBuilder buffer = new StringBuilder();
            using (var writer = new StringWriter(buffer))
            {
                this.GenerateUsings(codeNamespace);

                var generatedclass = new CodeTypeDeclaration("ReportMetadata");
                generatedclass.Comments.Add(
                    new CodeCommentStatement(
                        "This class is generated, any modification will be lost when the source file is altered"));
                generatedclass.BaseTypes.Add(typeof(ReportMetadataBase).Name);
                generatedclass.CustomAttributes.Add(
                    new CodeAttributeDeclaration(typeof(ReportMetadataAttribute).Name, 
                        new CodeAttributeArgument("Name", new CodePrimitiveExpression(_inputfile)),
                        new CodeAttributeArgument("Path", new CodePrimitiveExpression(_info.CodeFilePath.Substring(_info.ProjectFolder.Length)))
                        )
                    );
                generatedclass.IsPartial = false;
                generatedclass.TypeAttributes |= TypeAttributes.Public;
                codeNamespace.Types.Add(generatedclass);

                GenerateInitializeMethod(generatedclass);

                _info.Provider.GenerateCodeFromNamespace(codeNamespace, writer, GenerationOptions());
            }

            return buffer.ToString();
        }

        private void GenerateInitializeMethod(CodeTypeDeclaration generatedclass)
        {
            CodeMemberMethod memberMethod = new CodeMemberMethod();
            memberMethod.Attributes = MemberAttributes.Public | MemberAttributes.Override;            
            memberMethod.Name = "Initialize";
            memberMethod.ReturnType = new CodeTypeReference(typeof(void));
            generatedclass.Members.Add(memberMethod);

            foreach (var item in _data)
            {
                string methName = "AddFile";
                if (item.Kind == ReportItemKind.Folder)
                {
                    methName = "AddFolder";
                }
                memberMethod.Statements.Add(
                    new CodeMethodInvokeExpression(null, methName, new CodePrimitiveExpression(item.Name)));
            }
        }

        private void GenerateUsings(CodeNamespace codeNamespace)
        {
            codeNamespace.Imports.Add(new CodeNamespaceImport("Expect.Core"));
            codeNamespace.Imports.Add(new CodeNamespaceImport("Expect.Core.Reports"));
        }

        private System.CodeDom.Compiler.CodeGeneratorOptions GenerationOptions()
        {
            CodeGeneratorOptions options = new CodeGeneratorOptions();
            options.BracingStyle = "C";
            return options;
        }

    }
}
